home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Files / Standard File Samples / StandardGetFolder C ƒ / StandardGetFolder.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-07-03  |  10.1 KB  |  394 lines  |  [TEXT/CWIE]

  1. #include "StandardGetFolder.h"
  2.  
  3. #include <ToolUtils.h>
  4. #include <Folders.h>
  5. #include <StandardFile.h>
  6. #include <Aliases.h>
  7.  
  8. enum {
  9.     kSelectItem = 10,             // select button item number
  10.     kSFGetFolderDlgID = 250,    // dialog resource number
  11.     kStrListID = 250,            // our strings
  12.     kSelectStrNum = 1,            // word 'Select: ' for button
  13.     kDesktopStrNum = 2,            // word 'Desktop' for button
  14.     kSelectNoQuoteStrNum = 3,    // word 'Select: ' for button
  15.     
  16.     kUseQuotes = true,            // parameter for SetButtonName
  17.     kDontUseQuotes = false
  18. };
  19.  
  20. // the data we need to pass to our standard file hook routine
  21. // includes a pointer to the dialog, a pointer to the standard
  22. // file reply record (so we can inspect the current selection)
  23. // and a copy of the "previous" file spec of the reply record
  24. // so we can see if the selection has changed
  25.  
  26. struct UserDataRec {
  27.     StandardFileReply    *sfrPtr;
  28.     FSSpec                oldSelectionFSSpec;
  29.     DialogPtr            theDlgPtr;
  30. };
  31. typedef struct UserDataRec
  32.     UserDataRec, *UserDataRecPtr;
  33.  
  34. static void GetLabelString(StringPtr theStr, short stringNum)
  35. {
  36.     GetIndString(theStr, kStrListID, stringNum);
  37. }
  38.  
  39. static void CopyPStr(StringPtr src, StringPtr dest)
  40. {
  41.     BlockMoveData(src, dest, 1 + src[0]);
  42. }
  43.  
  44. static char GetSelectKey(void)
  45. {
  46.     // this is the key used to trigger the select button
  47.     
  48.     // NOT INTERNATIONAL SAVVY; should at least grab it from resources
  49.     
  50.     return 's';
  51. }
  52.  
  53. // SetButtonName sets the name of the Select button in the dialog
  54. //
  55. // To do this, we need to call the Script Manager to truncate the
  56. // label in the middle to fit the button and to merge the button
  57. // name with the word Select (possibly followed by quotes).  Using
  58. // the Script Manager avoids all sorts of problems internationally.
  59. //
  60. // buttonName is the name to appear following the word Select
  61. // quoteFlag should be true if the name is to appear in quotes
  62.  
  63. static void SetButtonName(DialogPtr theDlgPtr, short buttonID, StringPtr buttonName,
  64.                     Boolean quoteFlag)
  65. {
  66.     short     buttonType;
  67.     Handle    buttonHandle;
  68.     Rect    buttonRect;
  69.     short    textWidth;
  70.     Handle    labelHandle;
  71.     Handle    nameHandle;
  72.     Str15    keyStr;
  73.     Str255    labelStr;
  74.     OSErr    err;
  75.     
  76.     nameHandle = nil;
  77.     labelHandle = nil;
  78.     
  79.     // get the details of the button from the dialog
  80.     
  81.     GetDialogItem(theDlgPtr, buttonID, &buttonType, &buttonHandle, &buttonRect);
  82.     
  83.     // get the string for the select button label, "Select ^0" or "Select “^0”"
  84.     
  85.     GetLabelString(labelStr, (quoteFlag == kUseQuotes) ? kSelectStrNum : kSelectNoQuoteStrNum);
  86.     
  87.     // make string handles containing the select button label and the
  88.     // file name to be stuffed into the button
  89.     
  90.     err = PtrToHand(&labelStr[1], &labelHandle, labelStr[0]);
  91.     if (err != noErr) goto Bail;
  92.     
  93.     // cut out the middle of the file name to fit the button
  94.     //
  95.     // we'll temporarily use labelStr here to hold the modified button name
  96.     // since we don't own the buttonName string storage space
  97.     
  98.     textWidth = (buttonRect.right - buttonRect.left) - StringWidth(labelStr);
  99.  
  100.     CopyPStr(buttonName, labelStr);
  101.     (void) TruncString(textWidth, labelStr, smTruncMiddle);
  102.     
  103.     err = PtrToHand(&labelStr[1], &nameHandle, labelStr[0]);
  104.     if (err != noErr) goto Bail;
  105.     
  106.     // replace the ^0 in the Select string with the file name
  107.     
  108.     CopyPStr("\p^0", keyStr);
  109.     
  110.     (void) ReplaceText(labelHandle, nameHandle, keyStr);
  111.     
  112.     labelStr[0] = (unsigned char) GetHandleSize(labelHandle);
  113.     BlockMoveData(*labelHandle, &labelStr[1], labelStr[0]);
  114.     
  115.     // now set the control title, and re-validate the area
  116.     // above the control to avoid a needless redraw
  117.     
  118.     SetControlTitle((ControlHandle) buttonHandle, labelStr);
  119.     
  120.     ValidRect(&buttonRect);
  121.  
  122. Bail:
  123.     if (nameHandle)        DisposeHandle(nameHandle);
  124.     if (labelHandle)    DisposeHandle(labelHandle);
  125.     
  126. }
  127.  
  128. // FlashButton briefly highlights the dialog button 
  129. // as feedback for key equivalents
  130.  
  131. static void FlashButton(DialogPtr theDlgPtr, short buttonID)
  132. {
  133.     short    buttonType;
  134.     Handle    buttonHandle;
  135.     Rect    buttonRect;
  136.     long    finalTicks;
  137.     
  138.     GetDialogItem(theDlgPtr, buttonID, &buttonType, &buttonHandle, &buttonRect);
  139.     HiliteControl((ControlHandle) buttonHandle, kInButtonControlPart);
  140.     Delay(10, &finalTicks);
  141.     HiliteControl((ControlHandle) buttonHandle, 0);
  142. }
  143.  
  144. static Boolean SameFSSpec(FSSpecPtr spec1, FSSpecPtr spec2)
  145. {
  146.     return (spec1->vRefNum == spec2->vRefNum
  147.             && spec1->parID == spec2->parID
  148.             && EqualString(spec1->name, spec2->name, false, false));
  149. }
  150.  
  151. // MyModalDialogFilter maps a key to the Select button, and handles
  152. // flashing of the button when the key is hit
  153.  
  154. static pascal Boolean SFGetFolderModalDialogFilter(DialogPtr theDlgPtr, EventRecord *eventRec,
  155.                                             short *item, Ptr dataPtr)
  156. {
  157. #pragma unused (dataPtr)
  158.  
  159.     // make certain the proper dialog is showing, 'cause standard file
  160.     // can nest dialogs but calls the same filter for each
  161.     
  162.     if (((WindowPeek) theDlgPtr)->refCon == sfMainDialogRefCon)
  163.     {
  164.         // check if the select button was hit
  165.         
  166.         if ((eventRec->what == keyDown)
  167.             && (eventRec->modifiers & cmdKey) 
  168.             && ((eventRec->message & charCodeMask) == GetSelectKey()))
  169.         {
  170.             *item = kSelectItem;
  171.             FlashButton(theDlgPtr, kSelectItem);
  172.             return true;
  173.         }
  174.     }
  175.         
  176.     return false;
  177. }
  178.  
  179.  
  180. // MyDlgHook is a hook routine that maps the select button to Open
  181. // and sets the Select button name
  182.  
  183. static pascal short SFGetFolderDialogHook(short item, DialogPtr theDlgPtr, Ptr dataPtr)
  184. {
  185.     UserDataRecPtr    theUserDataRecPtr;
  186.     long            desktopDirID;
  187.     short            desktopVRefNum;
  188.     FSSpec            tempSpec;
  189.     Str63            desktopName;
  190.     OSErr            err;
  191.     
  192.     // be sure Std File is really showing us the intended dialog,
  193.     // not a nested modal dialog
  194.     
  195.     if (((WindowPeek) theDlgPtr)->refCon != sfMainDialogRefCon)
  196.     {
  197.         return item;
  198.     }
  199.     
  200.     theUserDataRecPtr = (UserDataRecPtr) dataPtr;
  201.     
  202.     // map the Select button to Open
  203.     
  204.     if (item == kSelectItem)
  205.     {
  206.         item = sfItemOpenButton;
  207.     }
  208.     
  209.     // find the desktop folder
  210.     
  211.     err = FindFolder(theUserDataRecPtr->sfrPtr->sfFile.vRefNum,
  212.                     kDesktopFolderType, kDontCreateFolder,
  213.                     &desktopVRefNum, &desktopDirID);
  214.     
  215.     if (err != noErr)
  216.     {
  217.         // for errors, get value that won't match any real vRefNum/dirID
  218.         desktopVRefNum = 0;
  219.         desktopDirID = 0;
  220.     }
  221.     
  222.     // change the Select button label if the selection has changed or
  223.     // if this is the first call to the hook
  224.     
  225.     if (item == sfHookFirstCall
  226.         || item == sfHookChangeSelection
  227.         || item == sfHookRebuildList
  228.         || ! SameFSSpec(&theUserDataRecPtr->sfrPtr->sfFile,
  229.                     &theUserDataRecPtr->oldSelectionFSSpec))
  230.     {
  231.         // be sure there is a file name selected
  232.         
  233.         if (theUserDataRecPtr->sfrPtr->sfFile.name[0] != '\0')
  234.         {
  235.             SetButtonName(theDlgPtr, kSelectItem, 
  236.                             theUserDataRecPtr->sfrPtr->sfFile.name, 
  237.                             kUseQuotes);    // true -> use quotes
  238.         }
  239.         else
  240.         {
  241.             // is the desktop selected?
  242.             
  243.             if (theUserDataRecPtr->sfrPtr->sfFile.vRefNum == desktopVRefNum
  244.                 && theUserDataRecPtr->sfrPtr->sfFile.parID == desktopDirID)
  245.             {
  246.                 // set button to "Select Desktop"
  247.                 
  248.                 GetLabelString(desktopName, kDesktopStrNum);
  249.                 SetButtonName(theDlgPtr, kSelectItem, 
  250.                                 desktopName, kDontUseQuotes);    // false -> no quotes
  251.             }
  252.             else
  253.             {
  254.                 // get parent directory's name for the Select button
  255.                 //
  256.                 // passing an empty name string to FSMakeFSSpec gets the
  257.                 // name of the folder specified by the parID parameter
  258.                 
  259.                 (void) FSMakeFSSpec(theUserDataRecPtr->sfrPtr->sfFile.vRefNum,
  260.                     theUserDataRecPtr->sfrPtr->sfFile.parID, "\p",
  261.                     &tempSpec);
  262.                 SetButtonName(theDlgPtr, kSelectItem, 
  263.                             tempSpec.name, kUseQuotes); // true -> use quotes
  264.             }
  265.         }
  266.     }
  267.     
  268.     // save the current selection as the old selection for comparison next time
  269.     //
  270.     // it's not valid on the first call, though, or if we don't have a 
  271.     // name available from standard file
  272.     
  273.     if (item != sfHookFirstCall || theUserDataRecPtr->sfrPtr->sfFile.name[0] != '\0')
  274.     {
  275.         theUserDataRecPtr->oldSelectionFSSpec = theUserDataRecPtr->sfrPtr->sfFile;
  276.     }
  277.     else
  278.     {
  279.         // on first call, empty string won't set the button correctly, 
  280.         // so invalidate oldSelection
  281.         
  282.         theUserDataRecPtr->oldSelectionFSSpec.vRefNum = 999;
  283.         theUserDataRecPtr->oldSelectionFSSpec.parID = 0;
  284.     }
  285.     
  286.     return item;
  287. }
  288.  
  289.  
  290.  
  291.  
  292.  
  293.  
  294.  
  295. void StandardGetFolder(FileFilterYDUPP fileFilter, StandardFileReply *theSFR)
  296. {
  297.     Point                 thePt;
  298.     SFTypeList            mySFTypeList;
  299.     UserDataRec            myData;
  300.     FSSpec                tempSpec;
  301.     Boolean                folderFlag;
  302.     Boolean                wasAliasedFlag;
  303.     DlgHookYDUPP        dlgHookUPP;
  304.     ModalFilterYDUPP    myModalFilterUPP;
  305.     OSErr                err;
  306.     
  307.     
  308.     // presumably we're running System 7 or later so CustomGetFile is
  309.     // available
  310.     
  311.     // set initial contents of Select button to a space
  312.     
  313.     CopyPStr("\p ", theSFR->sfFile.name);
  314.     
  315.     // point the user data parameter at the reply record so we can get to it later
  316.     
  317.     myData.sfrPtr = theSFR;
  318.     
  319.     // display the dialog
  320.     
  321.     dlgHookUPP = NewDlgHookYDProc(SFGetFolderDialogHook);
  322.     myModalFilterUPP = NewModalFilterYDProc(SFGetFolderModalDialogFilter);
  323.     
  324.     thePt.h = thePt.v = -1;    // center dialog
  325.     
  326.     CustomGetFile(fileFilter, 
  327.                     -1,                    // show all types
  328.                     mySFTypeList,
  329.                     theSFR,
  330.                     kSFGetFolderDlgID,
  331.                     thePt,                // top left point
  332.                     dlgHookUPP,
  333.                     myModalFilterUPP,
  334.                     nil,                // activate list
  335.                     nil,                // activate proc
  336.                     &myData);
  337.                     
  338.     DisposeRoutineDescriptor(dlgHookUPP);
  339.     DisposeRoutineDescriptor(myModalFilterUPP);
  340.     
  341.     // if cancel wasn't pressed and no fatal error occurred...
  342.     
  343.     if (theSFR->sfGood)
  344.     {
  345.         // if no name is in the reply record file spec,
  346.         // use the file spec of the parent folder
  347.         
  348.         if (theSFR->sfFile.name[0] == '\0')
  349.         {
  350.             err = FSMakeFSSpec(theSFR->sfFile.vRefNum, theSFR->sfFile.parID,
  351.                                 "\p", &tempSpec);
  352.             if (err == noErr)
  353.             {
  354.                 theSFR->sfFile = tempSpec;
  355.             }
  356.             else
  357.             {
  358.                 // no name to return, forget it
  359.                 
  360.                 theSFR->sfGood = false;
  361.             }
  362.         }
  363.         
  364.         // if there is now a name in the file spec, check if it's
  365.         // for a folder or a volume
  366.         
  367.         if (theSFR->sfFile.name[0] != '\0')
  368.         {
  369.             // the parID of the root of a disk is always fsRtParID == 1
  370.             
  371.             if (theSFR->sfFile.parID == fsRtParID)
  372.             {
  373.                 theSFR->sfIsVolume = true;
  374.                 theSFR->sfIsFolder = false;    // it would be reasonable for this to be true, too
  375.             }
  376.             
  377.             // we have a valid FSSpec, now let's make sure it's not for an alias file
  378.             
  379.             err = ResolveAliasFile(&theSFR->sfFile, true, &folderFlag, &wasAliasedFlag);
  380.             if (err != noErr)
  381.             {
  382.                 theSFR->sfGood = false;
  383.             }
  384.             
  385.             // did the alias resolve to a folder?
  386.             
  387.             if (folderFlag  && ! theSFR->sfIsVolume)
  388.             {
  389.                 theSFR->sfIsFolder = true;
  390.             }
  391.         }
  392.     }
  393. }
  394.